/* 
 * Copyright (c) [2010-2019] zhenfei.mzf@gmail.com rights reserved.
 * 
 * AntiSpy is licensed under the Mulan PSL v1.
 * You can use this software according to the terms and conditions of the Mulan PSL v1.
 * You may obtain a copy of Mulan PSL v1 at:
 *
 *     http://license.coscl.org.cn/MulanPSL
 *
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER
 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY OR
 * FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v1 for more details.
*/
// Free Disassembler and Assembler -- Assembler
//
// Copyright (C) 2001 Oleh Yuschuk
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

// 16.01.2002 - corrected error in processing of immediate constants.

#define _CRT_SECURE_NO_WARNINGS
#define STRICT
#pragma warning(disable:4996)  
#pragma warning(disable:4244)
#pragma warning(disable:4267)

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
//#include <dir.h>
#include <math.h>
#include <float.h>
#pragma hdrstop

#include "disasm.h"

////////////////////////////////////////////////////////////////////////////////
///////////////////////////// ASSEMBLER FUNCTIONS //////////////////////////////

// Scanner modes.
#define SA_NAME        0x0001          // Don't try to decode labels
#define SA_IMPORT      0x0002          // Allow import pseudolabel

// Types of input tokens reported by scanner.
#define SCAN_EOL       0               // End of line
#define SCAN_REG8      1               // 8-bit register
#define SCAN_REG16     2               // 16-bit register
#define SCAN_REG32     3               // 32-bit register
#define SCAN_SEG       4               // Segment register
#define SCAN_FPU       5               // FPU register
#define SCAN_MMX       6               // MMX register
#define SCAN_CR        7               // Control register
#define SCAN_DR        8               // Debug register
#define SCAN_OPSIZE    9               // Operand size modifier
#define SCAN_JMPSIZE   10              // Jump size modifier
#define SCAN_LOCAL     11              // Address on stack in form LOCAL.decimal
#define SCAN_ARG       12              // Address on stack in form ARG.decimal
#define SCAN_PTR       20              // PTR in MASM addressing statements
#define SCAN_REP       21              // REP prefix
#define SCAN_REPE      22              // REPE prefix
#define SCAN_REPNE     23              // REPNE prefix
#define SCAN_LOCK      24              // LOCK prefix
#define SCAN_NAME      25              // Command or label
#define SCAN_ICONST    26              // Hexadecimal constant
#define SCAN_DCONST    27              // Decimal constant
#define SCAN_OFS       28              // Undefined constant
#define SCAN_FCONST    29              // Floating-point constant
#define SCAN_EIP       30              // Register EIP
#define SCAN_SIGNED    31              // Keyword "SIGNED" (in expressions)
#define SCAN_UNSIGNED  32              // Keyword "UNSIGNED" (in expressions)
#define SCAN_CHAR      33              // Keyword "CHAR" (in expressions)
#define SCAN_FLOAT     34              // Keyword "FLOAT" (in expressions)
#define SCAN_DOUBLE    35              // Keyword "DOUBLE" (in expressions)
#define SCAN_FLOAT10   36              // Keyword "FLOAT10" (in expressions)
#define SCAN_STRING    37              // Keyword "STRING" (in expressions)
#define SCAN_UNICODE   38              // Keyword "UNICODE" (in expressions)
#define SCAN_MSG       39              // Pseudovariable MSG (in expressions)

#define SCAN_SYMB      64              // Any other character
#define SCAN_IMPORT    65              // Import pseudolabel
#define SCAN_ERR       255             // Definitely bad item

// Definition used by Assembler to report command matching errors.
#define MA_JMP         0x0001          // Invalid jump size modifier
#define MA_NOP         0x0002          // Wrong number of operands
#define MA_TYP         0x0004          // Bad operand type
#define MA_NOS         0x0008          // Explicit operand size expected
#define MA_SIZ         0x0010          // Bad operand size
#define MA_DIF         0x0020          // Different operand sizes
#define MA_SEG         0x0040          // Invalid segment register
#define MA_RNG         0x0080          // Constant out of expected range

typedef struct t_asmoperand {
	int            type;                 // Operand type, see beginning of file
	int            size;                 // Operand size or 0 if yet unknown
	int            index;                // Index or other register
	int            scale;                // Scale
	int            base;                 // Base register if present
	long           offset;               // Immediate value or offset
	int            anyoffset;            // Offset is present but undefined
	int            segment;              // Segment in address if present
	int            jmpmode;              // Specified jump size
} t_asmoperand;

static char      *asmcmd;              // Pointer to 0-terminated source line
static int       scan;                 // Type of last scanned element
static int       prio;                 // Priority of operation (0: highest)
static char      sdata[TEXTLEN];       // Last scanned name (depends on type)
static long      idata;                // Last scanned value
static long      double fdata;         // Floating-point number
static char      *asmerror;            // Explanation of last error, or NULL

// Simple and slightly recursive scanner shared by Assemble(). The scanner is
// straightforward and ineffective, but high speed is not a must here. As
// input, it uses global pointer to source line asmcmd. On exit, it fills in
// global variables scan, prio, sdata, idata and/or fdata. If some error is
// detected, asmerror points to error message, otherwise asmerror remains
// unchanged.
static void Scanasm(int mode) {
	int i,j,base,maxdigit;
	long decimal,hex;
	long double floating,divisor;
	char s[TEXTLEN],*pcmd;
	sdata[0]='\0';
	idata=0;
	if (asmcmd==NULL) {
		asmerror="NULL input line"; scan=SCAN_ERR; return; };
		while (*asmcmd==' ' || *asmcmd=='\t')
			asmcmd++;                          // Skip leading spaces
		if (*asmcmd=='\0' || *asmcmd==';') {
			scan=SCAN_EOL; return; };          // Empty line
			if (isalpha(*asmcmd) || *asmcmd=='_' || *asmcmd=='@') {
				sdata[0]=*asmcmd++; i=1;           // Some keyword or identifier
				while ((isalnum(*asmcmd) || *asmcmd=='_' || *asmcmd=='@') &&
					i<sizeof(sdata))
					sdata[i++]=*asmcmd++;
				if (i>=sizeof(sdata)) {
					asmerror="Too long identifier"; scan=SCAN_ERR; return; };
					sdata[i]='\0';
					while (*asmcmd==' ' || *asmcmd=='\t')
						asmcmd++;                        // Skip trailing spaces
					strcpy(s,sdata); strupr(s);
					for (j=0; j<=8; j++) {             // j==8 means "any register"
						if (strcmp(s,regname[0][j])!=0) continue;
						idata=j; scan=SCAN_REG8;         // 8-bit register
						return; };
						for (j=0; j<=8; j++) {
							if (strcmp(s,regname[1][j])!=0) continue;
							idata=j; scan=SCAN_REG16;        // 16-bit register
							return; };
							for (j=0; j<=8; j++) {
								if (strcmp(s,regname[2][j])!=0) continue;
								idata=j; scan=SCAN_REG32;        // 32-bit register
								return; };
								for (j=0; j<6; j++) {
									if (strcmp(s,segname[j])!=0) continue;
									idata=j; scan=SCAN_SEG;          // Segment register
									while (*asmcmd==' ' || *asmcmd=='\t')
										asmcmd++;                      // Skip trailing spaces
									return; };
									if (strcmp(s,"ST")==0) {
										pcmd=asmcmd; Scanasm(SA_NAME);   // FPU register
										if (scan!=SCAN_SYMB || idata!='(') {
											asmcmd=pcmd;                   // Undo last scan
											idata=0; scan=SCAN_FPU; return; };
											Scanasm(SA_NAME); j=idata;
											if ((scan!=SCAN_ICONST && scan!=SCAN_DCONST) || idata<0 || idata>7) {
												asmerror="FPU registers have indexes 0 to 7";
												scan=SCAN_ERR; return; };
												Scanasm(SA_NAME);
												if (scan!=SCAN_SYMB || idata!=')') {
													asmerror="Closing parenthesis expected";
													scan=SCAN_ERR; return; };
													idata=j; scan=SCAN_FPU; return; };
													for (j=0; j<=8; j++) {
														if (strcmp(s,fpuname[j])!=0) continue;
														idata=j; scan=SCAN_FPU;          // FPU register (alternative coding)
														return; };
														for (j=0; j<=8; j++) {
															if (strcmp(s,mmxname[j])!=0) continue;
															idata=j; scan=SCAN_MMX;          // MMX register
															return; };
															for (j=0; j<=8; j++) {
																if (strcmp(s,crname[j])!=0) continue;
																idata=j; scan=SCAN_CR;           // Control register
																return; };
																for (j=0; j<=8; j++) {
																	if (strcmp(s,drname[j])!=0) continue;
																	idata=j; scan=SCAN_DR;           // Debug register
																	return; };
																	for (j=0; j<sizeof(sizename)/sizeof(sizename[0]); j++) {
																		if (strcmp(s,sizename[j])!=0) continue;
																		pcmd=asmcmd; Scanasm(SA_NAME);
																		if (scan!=SCAN_PTR)              // Fetch non-functional "PTR"
																			asmcmd=pcmd;
																		idata=j; scan=SCAN_OPSIZE;       // Operand (data) size in bytes
																		return; };
																		if (strcmp(s,"EIP")==0) {          // Register EIP
																			scan=SCAN_EIP; idata=0; return; };
																			if (strcmp(s,"SHORT")==0) {        // Relative jump has 1-byte offset
																				scan=SCAN_JMPSIZE; idata=1; return; };
																				if (strcmp(s,"LONG")==0) {         // Relative jump has 4-byte offset
																					scan=SCAN_JMPSIZE; idata=2; return; };
																					if (strcmp(s,"NEAR")==0) {         // Jump within same code segment
																						scan=SCAN_JMPSIZE; idata=4; return; };
																						if (strcmp(s,"FAR")==0) {          // Jump to different code segment
																							scan=SCAN_JMPSIZE; idata=8; return; };
																							if (strcmp(s,"LOCAL")==0 && *asmcmd=='.') {
																								asmcmd++;
																								while (*asmcmd==' ' || *asmcmd=='\t')
																									asmcmd++;                      // Skip trailing spaces
																								if (!isdigit(*asmcmd)) {
																									asmerror="Integer number expected";
																									scan=SCAN_ERR; return; };
																									while (isdigit(*asmcmd))         // LOCAL index is decimal number!
																										idata=idata*10+(*asmcmd++)-'0';
																									scan=SCAN_LOCAL; return; };
																									if (strcmp(s,"ARG")==0 && *asmcmd=='.') {
																										asmcmd++;
																										while (*asmcmd==' ' || *asmcmd=='\t')
																											asmcmd++;                      // Skip trailing spaces
																										if (!isdigit(*asmcmd)) {
																											asmerror="Integer number expected";
																											scan=SCAN_ERR; return; };
																											while (isdigit(*asmcmd))         // ARG index is decimal number!
																												idata=idata*10+(*asmcmd++)-'0';
																											scan=SCAN_ARG; return; };
																											if (strcmp(s,"REP")==0) {
																												scan=SCAN_REP; return; };        // REP prefix
																												if (strcmp(s,"REPE")==0 || strcmp(s,"REPZ")==0) {
																													scan=SCAN_REPE; return; };       // REPE prefix
																													if (strcmp(s,"REPNE")==0 || strcmp(s,"REPNZ")==0) {
																														scan=SCAN_REPNE; return; };      // REPNE prefix
																														if (strcmp(s,"LOCK")==0) {
																															scan=SCAN_LOCK; return; };       // LOCK prefix
																															if (strcmp(s,"PTR")==0) {
																																scan=SCAN_PTR; return; };        // PTR in MASM addressing statements
																																if (strcmp(s,"CONST")==0 || strcmp(s,"OFFSET")==0) {
																																	scan=SCAN_OFS; return; };        // Present but undefined offset/constant
																																	if (strcmp(s,"SIGNED")==0) {
																																		scan=SCAN_SIGNED; return; };     // Keyword "SIGNED" (in expressions)
																																		if (strcmp(s,"UNSIGNED")==0) {
																																			scan=SCAN_UNSIGNED; return; };   // Keyword "UNSIGNED" (in expressions)
																																			if (strcmp(s,"CHAR")==0) {
																																				scan=SCAN_CHAR; return; };       // Keyword "CHAR" (in expressions)
																																				if (strcmp(s,"FLOAT")==0) {
																																					scan=SCAN_FLOAT; return; };      // Keyword "FLOAT" (in expressions)
																																					if (strcmp(s,"DOUBLE")==0) {
																																						scan=SCAN_DOUBLE; return; };     // Keyword "DOUBLE" (in expressions)
																																						if (strcmp(s,"FLOAT10")==0) {
																																							scan=SCAN_FLOAT10; return; };    // Keyword "FLOAT10" (in expressions)
																																							if (strcmp(s,"STRING")==0) {
																																								scan=SCAN_STRING; return; };     // Keyword "STRING" (in expressions)
																																								if (strcmp(s,"UNICODE")==0) {
																																									scan=SCAN_UNICODE; return; };    // Keyword "UNICODE" (in expressions)
																																									if (strcmp(s,"MSG")==0) {
																																										scan=SCAN_MSG; return; };        // Pseudovariable MSG (in expressions)
																																										if (mode & SA_NAME) {
																																											idata=i; scan=SCAN_NAME;         // Don't try to decode symbolic label
																																											return; }
																																										asmerror="Unknown identifier";
																																										scan=SCAN_ERR; return; }
			else if (isdigit(*asmcmd)) {         // Constant
				base=0; maxdigit=0; decimal=hex=0L; floating=0.0;
				if (asmcmd[0]=='0' && toupper(asmcmd[1])=='X') {
					base=16; asmcmd+=2; };           // Force hexadecimal number
					while (1) {
						if (isdigit(*asmcmd)) {
							decimal=decimal*10+(*asmcmd)-'0';
							floating=floating*10.0+(*asmcmd)-'0';
							hex=hex*16+(*asmcmd)-'0';
							if (maxdigit==0) maxdigit=9;
							asmcmd++; }
						else if (isxdigit(*asmcmd)) {
							hex=hex*16+toupper(*asmcmd++)-'A'+10;
							maxdigit=15; }
						else break; };
						if (maxdigit==0) {
							asmerror="Hexadecimal digits after 0x... expected";
							scan=SCAN_ERR; return; };
							if (toupper(*asmcmd)=='H') {       // Force hexadecimal number
								if (base==16) {
									asmerror="Please don't mix 0xXXXX and XXXXh forms";
									scan=SCAN_ERR; return; };
									asmcmd++;
									idata=hex; scan=SCAN_ICONST;
									while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++;
									return; };
									if (*asmcmd=='.') {                // Force decimal number
										if (base==16 || maxdigit>9) {
											asmerror="Not a decimal number"; scan=SCAN_ERR; return; };
											asmcmd++;
											if (isdigit(*asmcmd) || toupper(*asmcmd)=='E') {
												divisor=1.0;
												while (isdigit(*asmcmd)) {     // Floating-point number
													divisor/=10.0;
													floating+=divisor*(*asmcmd-'0');
													asmcmd++; };
													if (toupper(*asmcmd)=='E') {
														asmcmd++;
														if (*asmcmd=='-') { base=-1; asmcmd++; }
														else base=1;
														if (!isdigit(*asmcmd)) {
															asmerror="Invalid exponent"; scan=SCAN_ERR; return; };
															decimal=0;
															while (isdigit(*asmcmd)) {
																if (decimal<65536L) decimal=decimal*10+(*asmcmd++)-'0'; };
																floating*=powl(10,decimal*base); };
																fdata=floating;
																scan=SCAN_FCONST; return; }
											else {
												idata=decimal; scan=SCAN_DCONST;
												while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++;
												return;
											};
									};
									idata=hex; scan=SCAN_ICONST;       // Default is hexadecimal
									while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++;
									return; }
			else if (*asmcmd=='\'') {            // Character constant
				asmcmd++;
				if (*asmcmd=='\0' || (*asmcmd=='\\' && asmcmd[1]=='\0'))  {
					asmerror="Unterminated character constant"; scan=SCAN_ERR; return; };
					if (*asmcmd=='\'') {
						asmerror="Empty character constant"; scan=SCAN_ERR; return; };
						if (*asmcmd=='\\') asmcmd++;
						idata=*asmcmd++; 
						if (*asmcmd!='\'')  {
							asmerror="Unterminated character constant"; scan=SCAN_ERR; return; };
							asmcmd++;
							while (*asmcmd==' ' || *asmcmd=='\t') asmcmd++;
							scan=SCAN_ICONST; return; }
			else {                               // Any other character or combination
				idata=sdata[0]=*asmcmd++; sdata[1]=sdata[2]='\0';
				if (idata=='|' && *asmcmd=='|') {
					idata='||'; prio=10;             // '||'
					sdata[1]=*asmcmd++; }
				else if (idata=='&' && *asmcmd=='&') {
					idata='&&'; prio=9;              // '&&'
					sdata[1]=*asmcmd++; }
				else if (idata=='=' && *asmcmd=='=') {
					idata='=='; prio=5;              // '=='
					sdata[1]=*asmcmd++; }
				else if (idata=='!' && *asmcmd=='=') {
					idata='!='; prio=5;              // '!='
					sdata[1]=*asmcmd++; }
				else if (idata=='<' && *asmcmd=='=') {
					idata='<='; prio=4;              // '<='
					sdata[1]=*asmcmd++; }
				else if (idata=='>' && *asmcmd=='=') {
					idata='>='; prio=4;              // '>='
					sdata[1]=*asmcmd++; }
				else if (idata=='<' && *asmcmd=='<') {
					idata='<<'; prio=3;              // '<<'
					sdata[1]=*asmcmd++; }
				else if (idata=='>' && *asmcmd=='>') {
					idata='>>'; prio=3;              // '>>'
					sdata[1]=*asmcmd++; }
				else if (idata=='|') prio=8;       // '|'
				else if (idata=='^') prio=7;       // '^'
				else if (idata=='&') prio=6;       // '&'
				else if (idata=='<') {
					if (*asmcmd=='&') {              // Import pseudolabel (for internal use)
						if ((mode & SA_IMPORT)==0) {
							asmerror="Syntax error"; scan=SCAN_ERR; return; };
							asmcmd++; i=0;
							while (*asmcmd!='\0' && *asmcmd!='>') {
								sdata[i++]=*asmcmd++;
								if (i>=sizeof(sdata)) {
									asmerror="Too long import name"; scan=SCAN_ERR; return;
								};
							};
							if (*asmcmd!='>') {
								asmerror="Unterminated import name"; scan=SCAN_ERR; return; };
								asmcmd++; sdata[i]='\0';
								scan=SCAN_IMPORT; return; }
					else prio=4; }                   // '<'
				else if (idata=='>') prio=4;       // '>'
				else if (idata=='+') prio=2;       // '+'
				else if (idata=='-') prio=2;       // '-'
				else if (idata=='*') prio=1;       // '*'
				else if (idata=='/') prio=1;       // '/'
				else if (idata=='%') prio=1;       // '%'
				else if (idata==']') {
					pcmd=asmcmd; Scanasm(SA_NAME);
					if (scan!=SCAN_SYMB || idata!='[') {
						idata=']'; asmcmd=pcmd; prio=0; }
					else {
						idata='+'; prio=2;             // Translate '][' to '+'
					};
				}
				else prio=0;                       // Any other character
				scan=SCAN_SYMB;
				return;
			};
};

// Fetches one complete operand from the input line and fills in structure op
// with operand's data. Expects that first token of the operand is already
// scanned. Supports operands in generalized form (for example, R32 means any
// of general-purpose 32-bit integer registers).
static void Parseasmoperand(t_asmoperand *op) {
	int i,j,bracket,sign,xlataddr;
	int reg,r[9];
	long offset;
	if (scan==SCAN_EOL || scan==SCAN_ERR)
		return;                            // No or bad operand
	// Jump or call address may begin with address size modifier(s) SHORT, LONG,
	// NEAR and/or FAR. Not all combinations are allowed. After operand is
	// completely parsed, this function roughly checks whether modifier is
	// allowed. Exact check is done in Assemble().
	if (scan==SCAN_JMPSIZE) {
		j=0;
		while (scan==SCAN_JMPSIZE) {
			j|=idata;                        // Fetch all size modifiers
			Scanasm(0); };
			if (
				((j & 0x03)==0x03) ||            // Mixed SHORT and LONG
				((j & 0x0C)==0x0C) ||            // Mixed NEAR and FAR
				((j & 0x09)==0x09)               // Mixed FAR and SHORT
				) {
					asmerror="Invalid combination of jump address modifiers";
					scan=SCAN_ERR; return; };
					if ((j & 0x08)==0) j|=0x04;        // Force NEAR if not FAR
					op->jmpmode=j; };
					// Simple operands are either register or constant, their processing is
					// obvious and straightforward.
					if (scan==SCAN_REG8 || scan==SCAN_REG16 || scan==SCAN_REG32) {
						op->type=REG; op->index=idata;     // Integer general-purpose register
						if (scan==SCAN_REG8) op->size=1;
						else if (scan==SCAN_REG16) op->size=2;
						else op->size=4; }
					else if (scan==SCAN_FPU) {           // FPU register
						op->type=RST; op->index=idata; }
					else if (scan==SCAN_MMX) {           // MMX or 3DNow! register
						op->type=RMX; op->index=idata; }
					else if (scan==SCAN_CR) {            // Control register
						op->type=CRX; op->index=idata; }
					else if (scan==SCAN_DR) {            // Debug register
						op->type=DRX; op->index=idata; }
					else if (scan==SCAN_SYMB && idata=='-') {
						Scanasm(0);                        // Negative constant
						if (scan!=SCAN_ICONST && scan!=SCAN_DCONST && scan!=SCAN_OFS) {
							asmerror="Integer number expected";
							scan=SCAN_ERR; return; };
							op->type=IMM; op->offset=-idata;
							if (scan==SCAN_OFS) op->anyoffset=1; }
					else if (scan==SCAN_SYMB && idata=='+') {
						Scanasm(0);                        // Positive constant
						if (scan!=SCAN_ICONST && scan!=SCAN_DCONST && scan!=SCAN_OFS) {
							asmerror="Integer number expected";
							scan=SCAN_ERR; return; };
							op->type=IMM; op->offset=idata;
							if (scan==SCAN_OFS) op->anyoffset=1; }
					else if (scan==SCAN_ICONST || scan==SCAN_DCONST || scan==SCAN_OFS) {
						j=idata;
						if (scan==SCAN_OFS) op->anyoffset=1;
						Scanasm(0);
						if (scan==SCAN_SYMB && idata==':') {
							Scanasm(0);                      // Absolute long address (seg:offset)
							if (scan!=SCAN_ICONST && scan!=SCAN_DCONST && scan!=SCAN_OFS) {
								asmerror="Integer address expected";
								scan=SCAN_ERR; return; };
								op->type=JMF; op->offset=idata; op->segment=j;
								if (scan==SCAN_OFS) op->anyoffset=1; }
						else {
							op->type=IMM; op->offset=j;      // Constant without sign
							return;                          // Next token already scanned
						}; }
					else if (scan==SCAN_FCONST) {
						asmerror="Floating-point numbers are not allowed in command";
						scan=SCAN_ERR; return; }
					// Segment register or address.
					else if (scan==SCAN_SEG || scan==SCAN_OPSIZE ||
						(scan==SCAN_SYMB && idata=='[')
						) {                                  // Segment register or address
							bracket=0;
							if (scan==SCAN_SEG) {
								j=idata; Scanasm(0);
								if (scan!=SCAN_SYMB || idata!=':') {
									op->type=SGM; op->index=j;     // Segment register as operand
									return; };                     // Next token already scanned
									op->segment=j; Scanasm(0); };
									// Scan 32-bit address. This parser does not support 16-bit addresses.
									// First of all, get size of operand (optional), segment register (optional)
									// and opening bracket (required).
									while (1) {
										if (scan==SCAN_SYMB && idata=='[') {
											if (bracket) {                 // Bracket
												asmerror="Only one opening bracket allowed";
												scan=SCAN_ERR; return; };
												bracket=1; }
										else if (scan==SCAN_OPSIZE) {
											if (op->size!=0) {             // Size of operand
												asmerror="Duplicated size modifier";
												scan=SCAN_ERR; return; };
												op->size=idata; }
										else if (scan==SCAN_SEG) {
											if (op->segment!=SEG_UNDEF) {  // Segment register
												asmerror="Duplicated segment register";
												scan=SCAN_ERR; return; };
												op->segment=idata; Scanasm(0);
												if (scan!=SCAN_SYMB || idata!=':') {
													asmerror="Semicolon expected";
													scan=SCAN_ERR; return;
												}; }
										else if (scan==SCAN_ERR)
											return;
										else break;                      // None of expected address elements
										Scanasm(0); };
										if (bracket==0) {
											asmerror="Address expression requires brackets";
											scan=SCAN_ERR; return; };
											// Assembling a 32-bit address may be a kind of nigthmare, due to a large
											// number of allowed forms. Parser collects immediate offset in op->offset
											// and count for each register in array r[]. Then it decides whether this
											// combination is valid and determines scale, index and base. Assemble()
											// will use these numbers to select address form (with or without SIB byte,
											// 8- or 32-bit offset, use segment prefix or not). As a useful side effect
											// of this technique, one may specify, for example, [EAX*5] which will
											// correctly assemble to [EAX*4+EAX].
											for (i=0; i<=8; i++) r[i]=0;
											sign='+';                          // Default sign for the first operand
											xlataddr=0;
											while (1) {                        // Get SIB and offset
												if (scan==SCAN_SYMB && (idata=='+' || idata=='-')) {
													sign=idata; Scanasm(0); };
													if (scan==SCAN_ERR) return;
													if (sign=='?') {
														asmerror="Syntax error"; scan=SCAN_ERR; return; };
														// Register AL appears as part of operand of (seldom used) command XLAT.
														if (scan==SCAN_REG8 && idata==REG_EAX) {
															if (sign=='-') {
																asmerror="Unable to subtract register"; scan=SCAN_ERR; return; };
																if (xlataddr!=0) {
																	asmerror="Too many registers"; scan=SCAN_ERR; return; };
																	xlataddr=1;
																	Scanasm(0); }
														else if (scan==SCAN_REG16) {
															asmerror="Sorry, 16-bit addressing is not supported";
															scan=SCAN_ERR; return; }
														else if (scan==SCAN_REG32) {
															if (sign=='-') {
																asmerror="Unable to subtract register"; scan=SCAN_ERR; return; };
																reg=idata; Scanasm(0);
																if (scan==SCAN_SYMB && idata=='*') {
																	Scanasm(0);                  // Try index*scale
																	if (scan==SCAN_ERR) return;
																	if (scan==SCAN_OFS) {
																		asmerror="Undefined scale is not allowed"; scan=SCAN_ERR; return; };
																		if (scan!=SCAN_ICONST && scan!=SCAN_DCONST) {
																			asmerror="Syntax error"; scan=SCAN_ERR; return; };
																			if (idata==6 || idata==7 || idata>9) {
																				asmerror="Invalid scale"; scan=SCAN_ERR; return; };
																				r[reg]+=idata;
																				Scanasm(0); }
																else r[reg]++; }               // Simple register
														else if (scan==SCAN_LOCAL) {
															r[REG_EBP]++;
															op->offset-=idata*4;
															Scanasm(0); }
														else if (scan==SCAN_ARG) {
															r[REG_EBP]++;
															op->offset+=(idata+1)*4;
															Scanasm(0); }
														else if (scan==SCAN_ICONST || scan==SCAN_DCONST) {
															offset=idata; Scanasm(0);
															if (scan==SCAN_SYMB && idata=='*') {
																Scanasm(0);                  // Try scale*index
																if (scan==SCAN_ERR) return;
																if (sign=='-') {
																	asmerror="Unable to subtract register"; scan=SCAN_ERR; return; };
																	if (scan==SCAN_REG16) {
																		asmerror="Sorry, 16-bit addressing is not supported";
																		scan=SCAN_ERR; return; };
																		if (scan!=SCAN_REG32) {
																			asmerror="Syntax error"; scan=SCAN_ERR; return; };
																			if (offset==6 || offset==7 || offset>9) {
																				asmerror="Invalid scale"; scan=SCAN_ERR; return; };
																				r[idata]+=offset;
																				Scanasm(0); }
															else {
																if (sign=='-') op->offset-=offset;
																else op->offset+=offset;
															}; }
														else if (scan==SCAN_OFS) {
															Scanasm(0);
															if (scan==SCAN_SYMB && idata=='*') {
																asmerror="Undefined scale is not allowed"; scan=SCAN_ERR; return; }
															else {
																op->anyoffset=1;
															}; }
														else break;                      // None of expected address elements
														if (scan==SCAN_SYMB && idata==']') break;
														sign='?';
											};
											if (scan==SCAN_ERR) return;
											if (scan!=SCAN_SYMB || idata!=']') {
												asmerror="Syntax error";
												scan=SCAN_ERR; return; };
												// Process XLAT address separately.
												if (xlataddr!=0) {                 // XLAT address in form [EBX+AX]
													for (i=0; i<=8; i++) {           // Check which registers used
														if (i==REG_EBX) continue;
														if (r[i]!=0) break; };
														if (i<=8 || r[REG_EBX]!=1 || op->offset!=0 || op->anyoffset!=0) {
															asmerror="Invalid address"; scan=SCAN_ERR; return; };
															op->type=MXL; }
												// Determine scale, index and base.
												else {
													j=0;                             // Number of used registers
													for (i=0; i<=8; i++) {
														if (r[i]==0)
															continue;                    // Unused register
														if (r[i]==3 || r[i]==5 || r[i]==9) {
															if (op->index>=0 || op->base>=0) {
																if (j==0) asmerror="Invalid scale";
																else asmerror="Too many registers";
																scan=SCAN_ERR; return; };
																op->index=op->base=i;
																op->scale=r[i]-1; }
														else if (r[i]==2 || r[i]==4 || r[i]==8) {
															if (op->index>=0) {
																if (j<=1) asmerror="Only one register may be scaled";
																else asmerror="Too many registers";
																scan=SCAN_ERR; return; };
																op->index=i; op->scale=r[i]; }
														else if (r[i]==1) {
															if (op->base<0)
																op->base=i;
															else if (op->index<0) {
																op->index=i; op->scale=1; }
															else {
																asmerror="Too many registers";
																scan=SCAN_ERR; return;
															}; }
														else {
															asmerror="Invalid scale"; scan=SCAN_ERR; return; };
															j++;
													};
													op->type=MRG;
												}; }
					else {
						asmerror="Unrecognized operand"; scan=SCAN_ERR; return; };
						// In general, address modifier is allowed only with address expression which
						// is a constant, a far address or a memory expression. More precise check
						// will be done later in Assemble().
						if (op->jmpmode!=0 && op->type!=IMM && op->type!=JMF && op->type!=MRG) {
							asmerror="Jump address modifier is not allowed";
							scan=SCAN_ERR; return; };
							Scanasm(0);                          // Fetch next token from input line
};

// Function assembles text into 32-bit 80x86 machine code. It supports imprecise
// operands (for example, R32 stays for any general-purpose 32-bit register).
// This allows to search for incomplete commands. Command is precise when all
// significant bytes in model.mask are 0xFF. Some commands have more than one
// decoding. By calling Assemble() with attempt=0,1... and constsize=0,1,2,3 one
// gets also alternative variants (bit 0x1 of constsize is responsible for size
// of address constant and bit 0x2 - for immediate data). However, only one
// address form is generated ([EAX*2], but not [EAX+EAX]; [EBX+EAX] but not
// [EAX+EBX]; [EAX] will not use SIB byte; no DS: prefix and so on). Returns
// number of bytes in assembled code or non-positive number in case of detected
// error. This number is the negation of the offset in the input text where the
// error encountered. Unfortunately, BC 4.52 is unable to compile the switch
// (arg) in this code when any common subexpression optimization is on. The
// next #pragma statement disables all optimizations.

//#pragma option -Od                     // No optimizations, or BC 4.52 crashes

int Assemble(char *cmd,ulong ip,t_asmmodel *model,int attempt,
			 int constsize,char *errtext) {
				 int i,j,k,namelen,nameok,arg,match,datasize,addrsize,bytesize,minop,maxop;
				 int rep,lock,segment,jmpsize,jmpmode,longjump;
				 int hasrm,hassib,dispsize,immsize;
				 int anydisp,anyimm,anyjmp;
				 long l,displacement,immediate,jmpoffset;
				 char name[32],*nameend;
				 char tcode[MAXCMDSIZE],tmask[MAXCMDSIZE];
				 t_asmoperand aop[3],*op;             // Up to 3 operands allowed
				 const t_cmddata *pd;
				 if (model!=NULL) model->length=0;
				 if (cmd==NULL || model==NULL || errtext==NULL) {
					 if (errtext!=NULL) strcpy(errtext,"Internal OLLYDBG error");
					 return 0; };                       // Error in parameters
					 asmcmd=cmd;
					 rep=lock=0; errtext[0]='\0';
					 Scanasm(SA_NAME);
					 if (scan==SCAN_EOL)                  // End of line, nothing to assemble
						 return 0;
					 while (1) {                          // Fetch all REPxx and LOCK prefixes
						 if (scan==SCAN_REP || scan==SCAN_REPE || scan==SCAN_REPNE) {
							 if (rep!=0) {
								 strcpy(errtext,"Duplicated REP prefix"); goto error; };
								 rep=scan; }
						 else if (scan==SCAN_LOCK) {
							 if (lock!=0) {
								 strcpy(errtext,"Duplicated LOCK prefix"); goto error; };
								 lock=scan; }
						 else break;                        // No more prefixes
						 Scanasm(SA_NAME); };
						 if (scan!=SCAN_NAME || idata>16) {
							 strcpy(errtext,"Command mnemonic expected"); goto error; };
							 nameend=asmcmd;
							 strupr(sdata);
							 // Prepare full mnemonic (including repeat prefix, if any).
							 if (rep==SCAN_REP) sprintf(name,"REP %s",sdata);
							 else if (rep==SCAN_REPE) sprintf(name,"REPE %s",sdata);
							 else if (rep==SCAN_REPNE) sprintf(name,"REPNE %s",sdata);
							 else strcpy(name,sdata);
							 Scanasm(0);
							 // Parse command operands (up to 3). Note: jump address is always the first
							 // (and only) operand in actual command set.
							 for (i=0; i<3; i++) {
								 aop[i].type=NNN;                   // No operand
								 aop[i].size=0;                     // Undefined size
								 aop[i].index=-1;                   // No index
								 aop[i].scale=0;                    // No scale
								 aop[i].base=-1;                    // No base
								 aop[i].offset=0;                   // No offset
								 aop[i].anyoffset=0;                // No offset
								 aop[i].segment=SEG_UNDEF;          // No segment
								 aop[i].jmpmode=0; };               // No jump size modifier
								 Parseasmoperand(aop+0);
								 jmpmode=aop[0].jmpmode;
								 if (jmpmode!=0) jmpmode|=0x80;
								 if (scan==SCAN_SYMB && idata==',') {
									 Scanasm(0);
									 Parseasmoperand(aop+1);
									 if (scan==SCAN_SYMB && idata==',') {
										 Scanasm(0);
										 Parseasmoperand(aop+2);
									 };
								 };
								 if (scan==SCAN_ERR) {
									 strcpy(errtext,asmerror); goto error; };
									 if (scan!=SCAN_EOL) {
										 strcpy(errtext,"Extra input after operand"); goto error; };
										 // If jump size is not specified, function tries to use short jump. If
										 // attempt fails, it retries with long form.
										 longjump=0;                          // Try short jump on the first pass
retrylongjump:
										 nameok=0;
										 // Some commands allow different number of operands. Variables minop and
										 // maxop accumulate their minimal and maximal counts. The numbers are not
										 // used in assembly process but allow for better error diagnostics.
										 minop=3; maxop=0;
										 // Main assembly loop: try to find the command which matches all operands,
										 // but do not process operands yet.
										 namelen=strlen(name);
										 for (pd=cmddata; pd->mask!=0; pd++) {
											 if (pd->name[0]=='&') {            // Mnemonic depends on operand size
												 j=1;
												 datasize=2;
												 addrsize=4;
												 while (1) {                      // Try all mnemonics (separated by ':')
													 for (i=0; pd->name[j]!='\0' && pd->name[j]!=':'; j++) {
														 if (pd->name[j]=='*') {
															 if (name[i]=='W') { datasize=2; i++; }
															 else if (name[i]=='D') { datasize=4; i++; }
															 else if (sizesens==0) datasize=2;
															 else datasize=4; }
														 else if (pd->name[j]==name[i]) i++;
														 else break;
													 };
													 if (name[i]=='\0' && (pd->name[j]=='\0' || pd->name[j]==':'))
														 break;                       // Bingo!
													 while (pd->name[j]!='\0' && pd->name[j]!=':')
														 j++;
													 if (pd->name[j]==':') {
														 j++; datasize=4; }           // Retry with 32-bit mnenonic
													 else {
														 i=0; break;                  // Comparison failed
													 };
												 };
												 if (i==0) continue; }
											 else if (pd->name[0]=='$') {       // Mnemonic depends on address size
												 j=1;
												 datasize=0;
												 addrsize=2;
												 while (1) {                      // Try all mnemonics (separated by ':')
													 for (i=0; pd->name[j]!='\0' && pd->name[j]!=':'; j++) {
														 if (pd->name[j]=='*') {
															 if (name[i]=='W') { addrsize=2; i++; }
															 else if (name[i]=='D') { addrsize=4; i++; }
															 else if (sizesens==0) addrsize=2;
															 else addrsize=4; }
														 else if (pd->name[j]==name[i]) i++;
														 else break;
													 };
													 if (name[i]=='\0' && (pd->name[j]=='\0' || pd->name[j]==':'))
														 break;                       // Bingo!
													 while (pd->name[j]!='\0' && pd->name[j]!=':')
														 j++;
													 if (pd->name[j]==':') {
														 j++; addrsize=4; }           // Retry with 32-bit mnenonic
													 else {
														 i=0; break;                  // Comparison failed
													 };
												 };
												 if (i==0) continue; }
											 else {                             // Compare with all synonimes
												 j=k=0;
												 datasize=0;                      // Default settings
												 addrsize=4;
												 while (1) {
													 while (pd->name[j]!=',' && pd->name[j]!='\0') j++;
													 if (j-k==namelen && strnicmp(name,pd->name+k,namelen)==0) break;
													 k=j+1; if (pd->name[j]=='\0') break;
													 j=k; };
													 if (k>j) continue;
											 };
											 // For error diagnostics it is important to know whether mnemonic exists.
											 nameok++;
											 if (pd->arg1==NNN || pd->arg1>=PSEUDOOP)
												 minop=0;
											 else if (pd->arg2==NNN || pd->arg2>=PSEUDOOP) {
												 if (minop>1) minop=1;
												 if (maxop<1) maxop=1; }
											 else if (pd->arg3==NNN || pd->arg3>=PSEUDOOP) {
												 if (minop>2) minop=2;
												 if (maxop<2) maxop=2; }
											 else
												 maxop=3;
											 // Determine default and allowed operand size(s).
											 if (pd->bits==FF) datasize=2;      // Forced 16-bit size
											 if (pd->bits==WW || pd->bits==WS || pd->bits==W3 || pd->bits==WP)
												 bytesize=1;                      // 1-byte size allowed
											 else
												 bytesize=0;                      // Word/dword size only
											 // Check whether command operands match specified. If so, variable match
											 // remains zero, otherwise it contains kind of mismatch. This allows for
											 // better error diagnostics.
											 match=0;
											 for (j=0; j<3; j++) {              // Up to 3 operands
												 op=aop+j;
												 if (j==0) arg=pd->arg1;
												 else if (j==1) arg=pd->arg2;
												 else arg=pd->arg3;
												 if (arg==NNN || arg>=PSEUDOOP) {
													 if (op->type!=NNN)             // No more arguments
														 match|=MA_NOP;
													 break; };
													 if (op->type==NNN) {
														 match|=MA_NOP; break; };       // No corresponding operand
														 switch (arg) {
		case REG:                      // Integer register in Reg field
		case RCM:                      // Integer register in command byte
		case RAC:                      // Accumulator (AL/AX/EAX, implicit)
			if (op->type!=REG) match|=MA_TYP;
			if (arg==RAC && op->index!=REG_EAX && op->index!=8) match|=MA_TYP;
			if (bytesize==0 && op->size==1) match|=MA_SIZ;
			if (datasize==0) datasize=op->size;
			if (datasize!=op->size) match|=MA_DIF;
			break;
		case RG4:                      // Integer 4-byte register in Reg field
			if (op->type!=REG) match|=MA_TYP;
			if (op->size!=4) match|=MA_SIZ;
			if (datasize==0) datasize=op->size;
			if (datasize!=op->size) match|=MA_DIF;
			break;
		case RAX:                      // AX (2-byte, implicit)
			if (op->type!=REG || (op->index!=REG_EAX && op->index!=8))
				match|=MA_TYP;
			if (op->size!=2) match|=MA_SIZ;
			if (datasize==0) datasize=op->size;
			if (datasize!=op->size) match|=MA_DIF;
			break;
		case RDX:                      // DX (16-bit implicit port address)
			if (op->type!=REG || (op->index!=REG_EDX && op->index!=8))
				match|=MA_TYP;
			if (op->size!=2) match|=MA_SIZ; break;
		case RCL:                      // Implicit CL register (for shifts)
			if (op->type!=REG || (op->index!=REG_ECX && op->index!=8))
				match|=MA_TYP;
			if (op->size!=1) match|=MA_SIZ;
			break;
		case RS0:                      // Top of FPU stack (ST(0))
			if (op->type!=RST || (op->index!=0 && op->index!=8))
				match|=MA_TYP;
			break;
		case RST:                      // FPU register (ST(i)) in command byte
			if (op->type!=RST) match|=MA_TYP; break;
		case RMX:                      // MMX register MMx
		case R3D:                      // 3DNow! register MMx
			if (op->type!=RMX) match|=MA_TYP; break;
		case MRG:                      // Memory/register in ModRM byte
			if (op->type!=MRG && op->type!=REG) match|=MA_TYP;
			if (bytesize==0 && op->size==1) match|=MA_SIZ;
			if (datasize==0) datasize=op->size;
			if (op->size!=0 && op->size!=datasize) match|=MA_DIF;
			break;
		case MR1:                      // 1-byte memory/register in ModRM byte
			if (op->type!=MRG && op->type!=REG) match|=MA_TYP;
			if (op->size!=0 && op->size!=1) match|=MA_SIZ;
			break;
		case MR2:                      // 2-byte memory/register in ModRM byte
			if (op->type!=MRG && op->type!=REG) match|=MA_TYP;
			if (op->size!=0 && op->size!=2) match|=MA_SIZ;
			break;
		case MR4:                      // 4-byte memory/register in ModRM byte
			if (op->type!=MRG && op->type!=REG) match|=MA_TYP;
			if (op->size!=0 && op->size!=4) match|=MA_SIZ;
			break;
		case RR4:                      // 4-byte memory/register (register only)
			if (op->type!=REG) match|=MA_TYP;
			if (op->size!=0 && op->size!=4) match|=MA_SIZ;
			break;
		case MRJ:                      // Memory/reg in ModRM as JUMP target
			if (op->type!=MRG && op->type!=REG) match|=MA_TYP;
			if (op->size!=0 && op->size!=4) match|=MA_SIZ;
			if ((jmpmode & 0x09)!=0) match|=MA_JMP;
			jmpmode&=0x7F; break;
		case MR8:                      // 8-byte memory/MMX register in ModRM
		case MRD:                      // 8-byte memory/3DNow! register in ModRM
			if (op->type!=MRG && op->type!=RMX) match|=MA_TYP;
			if (op->size!=0 && op->size!=8) match|=MA_SIZ;
			break;
		case RR8:                      // 8-byte MMX register only in ModRM
		case RRD:                      // 8-byte memory/3DNow! (register only)
			if (op->type!=RMX) match|=MA_TYP;
			if (op->size!=0 && op->size!=8) match|=MA_SIZ;
			break;
		case MMA:                      // Memory address in ModRM byte for LEA
			if (op->type!=MRG) match|=MA_TYP; break;
		case MML:                      // Memory in ModRM byte (for LES)
			if (op->type!=MRG) match|=MA_TYP;
			if (op->size!=0 && op->size!=6) match|=MA_SIZ;
			if (datasize==0) datasize=4; else if (datasize!=4) match|=MA_DIF;
			break;
		case MMS:                      // Memory in ModRM byte (as SEG:OFFS)
			if (op->type!=MRG) match|=MA_TYP;
			if (op->size!=0 && op->size!=6) match|=MA_SIZ;
			if ((jmpmode & 0x07)!=0) match|=MA_JMP;
			jmpmode&=0x7F; break;
		case MM6:                      // Memory in ModRm (6-byte descriptor)
			if (op->type!=MRG) match|=MA_TYP;
			if (op->size!=0 && op->size!=6) match|=MA_SIZ;
			break;
		case MMB:                      // Two adjacent memory locations (BOUND)
			if (op->type!=MRG) match|=MA_TYP;
			k=op->size; if (ideal==0 && k>1) k/=2;
			if (k!=0 && k!=datasize) match|=MA_DIF;
			break;
		case MD2:                      // Memory in ModRM byte (16-bit integer)
		case MB2:                      // Memory in ModRM byte (16-bit binary)
			if (op->type!=MRG) match|=MA_TYP;
			if (op->size!=0 && op->size!=2) match|=MA_SIZ;
			break;
		case MD4:                      // Memory in ModRM byte (32-bit integer)
		case MF4:                      // Memory in ModRM byte (32-bit float)
			if (op->type!=MRG) match|=MA_TYP;
			if (op->size!=0 && op->size!=4) match|=MA_SIZ;
			break;
		case MD8:                      // Memory in ModRM byte (64-bit integer)
		case MF8:                      // Memory in ModRM byte (64-bit float)
			if (op->type!=MRG) match|=MA_TYP;
			if (op->size!=0 && op->size!=8) match|=MA_SIZ;
			break;
		case MDA:                      // Memory in ModRM byte (80-bit BCD)
		case MFA:                      // Memory in ModRM byte (80-bit float)
			if (op->type!=MRG) match|=MA_TYP;
			if (op->size!=0 && op->size!=10) match|=MA_SIZ;
			break;
		case MFE:                      // Memory in ModRM byte (FPU environment)
		case MFS:                      // Memory in ModRM byte (FPU state)
		case MFX:                      // Memory in ModRM byte (ext. FPU state)
			if (op->type!=MRG) match|=MA_TYP;
			if (op->size!=0) match|=MA_SIZ;
			break;
		case MSO:                      // Source in string operands ([ESI])
			if (op->type!=MRG || op->base!=REG_ESI ||
				op->index!=-1 || op->offset!=0 || op->anyoffset!=0) match|=MA_TYP;
			if (datasize==0) datasize=op->size;
			if (op->size!=0 && op->size!=datasize) match|=MA_DIF;
			break;
		case MDE:                      // Destination in string operands ([EDI])
			if (op->type!=MRG || op->base!=REG_EDI ||
				op->index!=-1 || op->offset!=0 || op->anyoffset!=0) match|=MA_TYP;
			if (op->segment!=SEG_UNDEF && op->segment!=SEG_ES) match|=MA_SEG;
			if (datasize==0) datasize=op->size;
			if (op->size!=0 && op->size!=datasize) match|=MA_DIF;
			break;
		case MXL:                      // XLAT operand ([EBX+AL])
			if (op->type!=MXL) match|=MA_TYP; break;
		case IMM:                      // Immediate data (8 or 16/32)
		case IMU:                      // Immediate unsigned data (8 or 16/32)
			if (op->type!=IMM) match|=MA_TYP;
			break;
		case VXD:                      // VxD service (32-bit only)
			if (op->type!=IMM) match|=MA_TYP;
			if (datasize==0) datasize=4;
			if (datasize!=4) match|=MA_SIZ;
			break;
		case JMF:                      // Immediate absolute far jump/call addr
			if (op->type!=JMF) match|=MA_TYP;
			if ((jmpmode & 0x05)!=0) match|=MA_JMP;
			jmpmode&=0x7F; break;
		case JOB:                      // Immediate byte offset (for jumps)
			if (op->type!=IMM || longjump) match|=MA_TYP;
			if ((jmpmode & 0x0A)!=0) match|=MA_JMP;
			jmpmode&=0x7F; break;
		case JOW:                      // Immediate full offset (for jumps)
			if (op->type!=IMM) match|=MA_TYP;
			if ((jmpmode & 0x09)!=0) match|=MA_JMP;
			jmpmode&=0x7F; break;
		case IMA:                      // Immediate absolute near data address
			if (op->type!=MRG || op->base>=0 || op->index>=0) match|=MA_TYP;
			break;
		case IMX:                      // Immediate sign-extendable byte
			if (op->type!=IMM) match|=MA_TYP;
			if (op->offset<-128 || op->offset>127) match|=MA_RNG;
			break;
		case C01:                      // Implicit constant 1 (for shifts)
			if (op->type!=IMM || (op->offset!=1 && op->anyoffset==0))
				match|=MA_TYP;
			break;
		case IMS:                      // Immediate byte (for shifts)
		case IM1:                      // Immediate byte
			if (op->type!=IMM) match|=MA_TYP;
			if (op->offset<-128 || op->offset>255) match|=MA_RNG;
			break;
		case IM2:                      // Immediate word (ENTER/RET)
			if (op->type!=IMM) match|=MA_TYP;
			if (op->offset<0 || op->offset>65535) match|=MA_RNG;
			break;
		case SGM:                      // Segment register in ModRM byte
			if (op->type!=SGM) match|=MA_TYP;
			if (datasize==0) datasize=2;
			if (datasize!=2) match|=MA_DIF;
			break;
		case SCM:                      // Segment register in command byte
			if (op->type!=SGM) match|=MA_TYP;
			break;
		case CRX:                      // Control register CRx
		case DRX:                      // Debug register DRx
			if (op->type!=arg) match|=MA_TYP;
			if (datasize==0) datasize=4;
			if (datasize!=4) match|=MA_DIF;
			break;
		case PRN:                      // Near return address (pseudooperand)
		case PRF:                      // Far return address (pseudooperand)
		case PAC:                      // Accumulator (AL/AX/EAX, pseudooperand)
		case PAH:                      // AH (in LAHF/SAHF, pseudooperand)
		case PFL:                      // Lower byte of flags (pseudooperand)
		case PS0:                      // Top of FPU stack (pseudooperand)
		case PS1:                      // ST(1) (pseudooperand)
		case PCX:                      // CX/ECX (pseudooperand)
		case PDI:                      // EDI (pseudooperand in MMX extentions)
			break;
		default:                       // Undefined type of operand
			strcpy(errtext,"Internal Assembler error");
			goto error;
														 };                               // End of switch (arg)
														 if ((jmpmode & 0x80)!=0) match|=MA_JMP;
														 if (match!=0) break;             // Some of the operands doesn't match
											 };                                 // End of operand matching loop
											 if (match==0) {                    // Exact match found
												 if (attempt>0) {
													 --attempt; nameok=0; }         // Well, try to find yet another match
												 else break;
											 };
										 };                                   // End of command search loop
										 // Check whether some error was detected. If several errors were found
										 // similtaneously, report one (roughly in order of significance).
										 if (nameok==0) {                     // Mnemonic unavailable
											 strcpy(errtext,"Unrecognized command");
											 asmcmd=nameend; goto error; };
											 if (match!=0) {                      // Command not found
												 if (minop>0 && aop[minop-1].type==NNN)
													 strcpy(errtext,"Too few operands");
												 else if (maxop<3 && aop[maxop].type!=NNN)
													 strcpy(errtext,"Too many operands");
												 else if (nameok>1)                 // More that 1 command
													 strcpy(errtext,"Command does not support given operands");
												 else if (match & MA_JMP)
													 strcpy(errtext,"Invalid jump size modifier");
												 else if (match & MA_NOP)
													 strcpy(errtext,"Wrong number of operands");
												 else if (match & MA_TYP)
													 strcpy(errtext,"Command does not support given operands");
												 else if (match & MA_NOS)
													 strcpy(errtext,"Please specify operand size");
												 else if (match & MA_SIZ)
													 strcpy(errtext,"Bad operand size");
												 else if (match & MA_DIF)
													 strcpy(errtext,"Different size of operands");
												 else if (match & MA_SEG)
													 strcpy(errtext,"Invalid segment register");
												 else if (match & MA_RNG)
													 strcpy(errtext,"Constant out of expected range");
												 else
													 strcpy(errtext,"Erroneous command");
												 goto error;
											 };
											 // Exact match found. Now construct the code.
											 hasrm=0;                             // Whether command has ModR/M byte
											 hassib=0;                            // Whether command has SIB byte
											 dispsize=0;                          // Size of displacement (if any)
											 immsize=0;                           // Size of immediate data (if any)
											 segment=SEG_UNDEF;                   // Necessary segment prefix
											 jmpsize=0;                           // No relative jumps
											 memset(tcode,0,sizeof(tcode));
											 *(ulong *)tcode=pd->code & pd->mask;
											 memset(tmask,0,sizeof(tmask));
											 *(ulong *)tmask=pd->mask;
											 i=pd->len-1;                         // Last byte of command itself
											 if (rep) i++;                        // REPxx prefixes count as extra byte
											 // In some cases at least one operand must have explicit size declaration (as
											 // in MOV [EAX],1). This preliminary check does not include all cases.
											 if (pd->bits==WW || pd->bits==WS || pd->bits==WP) {
												 if (datasize==0) {
													 strcpy(errtext,"Please specify operand size"); goto error; }
												 else if (datasize>1)
													 tcode[i]|=0x01;                  // WORD or DWORD size of operands
												 tmask[i]|=0x01; }
											 else if (pd->bits==W3) {
												 if (datasize==0) {
													 strcpy(errtext,"Please specify operand size"); goto error; }
												 else if (datasize>1)
													 tcode[i]|=0x08;                  // WORD or DWORD size of operands
												 tmask[i]|=0x08; };
												 // Present suffix of 3DNow! command as immediate byte operand.
												 if ((pd->type & C_TYPEMASK)==C_NOW) {
													 immsize=1;
													 immediate=(pd->code>>16) & 0xFF; };
													 // Process operands again, this time constructing the code.
													 anydisp=anyimm=anyjmp=0;
													 for (j=0; j<3; j++) {                // Up to 3 operands
														 op=aop+j;
														 if (j==0) arg=pd->arg1;
														 else if (j==1) arg=pd->arg2;
														 else arg=pd->arg3;
														 if (arg==NNN) break;               // All operands processed
														 switch (arg) {
	  case REG:                        // Integer register in Reg field
	  case RG4:                        // Integer 4-byte register in Reg field
	  case RMX:                        // MMX register MMx
	  case R3D:                        // 3DNow! register MMx
	  case CRX:                        // Control register CRx
	  case DRX:                        // Debug register DRx
		  hasrm=1;
		  if (op->index<8) {
			  tcode[i+1]|=(char)(op->index<<3); tmask[i+1]|=0x38; };
			  break;
	  case RCM:                        // Integer register in command byte
	  case RST:                        // FPU register (ST(i)) in command byte
		  if (op->index<8) {
			  tcode[i]|=(char)op->index; tmask[i]|=0x07; };
			  break;
	  case RAC:                        // Accumulator (AL/AX/EAX, implicit)
	  case RAX:                        // AX (2-byte, implicit)
	  case RDX:                        // DX (16-bit implicit port address)
	  case RCL:                        // Implicit CL register (for shifts)
	  case RS0:                        // Top of FPU stack (ST(0))
	  case MDE:                        // Destination in string op's ([EDI])
	  case C01:                        // Implicit constant 1 (for shifts)
		  break;                         // Simply skip implicit operands
	  case MSO:                        // Source in string op's ([ESI])
	  case MXL:                        // XLAT operand ([EBX+AL])
		  if (op->segment!=SEG_UNDEF && op->segment!=SEG_DS)
			  segment=op->segment;
		  break;
	  case MRG:                        // Memory/register in ModRM byte
	  case MRJ:                        // Memory/reg in ModRM as JUMP target
	  case MR1:                        // 1-byte memory/register in ModRM byte
	  case MR2:                        // 2-byte memory/register in ModRM byte
	  case MR4:                        // 4-byte memory/register in ModRM byte
	  case RR4:                        // 4-byte memory/register (register only)
	  case MR8:                        // 8-byte memory/MMX register in ModRM
	  case RR8:                        // 8-byte MMX register only in ModRM
	  case MRD:                        // 8-byte memory/3DNow! register in ModRM
	  case RRD:                        // 8-byte memory/3DNow! (register only)
		  hasrm=1;
		  if (op->type!=MRG) {           // Register in ModRM byte
			  tcode[i+1]|=0xC0; tmask[i+1]|=0xC0;
			  if (op->index<8) {
				  tcode[i+1]|=(char)op->index; tmask[i+1]|=0x07; };
				  break;
		  };                             // Note: NO BREAK, continue with address
	  case MMA:                        // Memory address in ModRM byte for LEA
	  case MML:                        // Memory in ModRM byte (for LES)
	  case MMS:                        // Memory in ModRM byte (as SEG:OFFS)
	  case MM6:                        // Memory in ModRm (6-byte descriptor)
	  case MMB:                        // Two adjacent memory locations (BOUND)
	  case MD2:                        // Memory in ModRM byte (16-bit integer)
	  case MB2:                        // Memory in ModRM byte (16-bit binary)
	  case MD4:                        // Memory in ModRM byte (32-bit integer)
	  case MD8:                        // Memory in ModRM byte (64-bit integer)
	  case MDA:                        // Memory in ModRM byte (80-bit BCD)
	  case MF4:                        // Memory in ModRM byte (32-bit float)
	  case MF8:                        // Memory in ModRM byte (64-bit float)
	  case MFA:                        // Memory in ModRM byte (80-bit float)
	  case MFE:                        // Memory in ModRM byte (FPU environment)
	  case MFS:                        // Memory in ModRM byte (FPU state)
	  case MFX:                        // Memory in ModRM byte (ext. FPU state)
		  hasrm=1; displacement=op->offset; anydisp=op->anyoffset;
		  if (op->base<0 && op->index<0) {
			  dispsize=4;                  // Special case of immediate address
			  if (op->segment!=SEG_UNDEF && op->segment!=SEG_DS)
				  segment=op->segment;
			  tcode[i+1]|=0x05;
			  tmask[i+1]|=0xC7; }
		  else if (op->index<0 && op->base!=REG_ESP) {
			  tmask[i+1]|=0xC0;            // SIB byte unnecessary
			  if (op->offset==0 && op->anyoffset==0 && op->base!=REG_EBP)
				  ;                          // [EBP] always requires offset
			  else if ((constsize & 1)!=0 &&
				  ((op->offset>=-128 && op->offset<128) || op->anyoffset!=0)
				  ) {
					  tcode[i+1]|=0x40;          // Disp8
					  dispsize=1; }
			  else {
				  tcode[i+1]|=0x80;          // Disp32
				  dispsize=4; };
				  if (op->base<8) {
					  if (op->segment!=SEG_UNDEF && op->segment!=addr32[op->base].defseg)
						  segment=op->segment;
					  tcode[i+1]|=
						  (char)op->base;          // Note that case [ESP] has base<0.
					  tmask[i+1]|=0x07; }
				  else segment=op->segment; }
		  else {                         // SIB byte necessary
			  hassib=1;
			  if (op->base==REG_EBP &&     // EBP as base requires offset, optimize
				  op->index>=0 && op->scale==1 && op->offset==0 && op->anyoffset==0) {
					  op->base=op->index; op->index=REG_EBP; };
					  if (op->index==REG_ESP &&    // ESP cannot be an index, reorder
						  op->scale<=1) {
							  op->index=op->base; op->base=REG_ESP; op->scale=1; };
							  if (op->base<0 &&            // No base means 4-byte offset, optimize
								  op->index>=0 && op->scale==2 &&
								  op->offset>=-128 && op->offset<128 && op->anyoffset==0) {
									  op->base=op->index; op->scale=1; };
									  if (op->index==REG_ESP) {    // Reordering was unsuccessfull
										  strcpy(errtext,"Invalid indexing mode");
										  goto error; };
										  if (op->base<0) {
											  tcode[i+1]|=0x04;
											  dispsize=4; }
										  else if (op->offset==0 && op->anyoffset==0 && op->base!=REG_EBP)
											  tcode[i+1]|=0x04;          // No displacement
										  else if ((constsize & 1)!=0 &&
											  ((op->offset>=-128 && op->offset<128) || op->anyoffset!=0)
											  ) {
												  tcode[i+1]|=0x44;          // Disp8
												  dispsize=1; }
										  else {
											  tcode[i+1]|=0x84;          // Disp32
											  dispsize=4; };
											  tmask[i+1]|=0xC7;            // ModRM completed, proceed with SIB
											  if (op->scale==2) tcode[i+2]|=0x40;
											  else if (op->scale==4) tcode[i+2]|=0x80;
											  else if (op->scale==8) tcode[i+2]|=0xC0;
											  tmask[i+2]|=0xC0;
											  if (op->index<8) {
												  if (op->index<0) op->index=0x04;
												  tcode[i+2]|=(char)(op->index<<3);
												  tmask[i+2]|=0x38; };
												  if (op->base<8) {
													  if (op->base<0) op->base=0x05;
													  if (op->segment!=SEG_UNDEF && op->segment!=addr32[op->base].defseg)
														  segment=op->segment;
													  tcode[i+2]|=(char)op->base;
													  tmask[i+2]|=0x07; }
												  else segment=op->segment; };
												  break;
	  case IMM:                        // Immediate data (8 or 16/32)
	  case IMU:                        // Immediate unsigned data (8 or 16/32)
	  case VXD:                        // VxD service (32-bit only)
		  if (datasize==0 && pd->arg2==NNN && (pd->bits==SS || pd->bits==WS))
			  datasize=4;
		  if (datasize==0) {
			  strcpy(errtext,"Please specify operand size");
			  goto error; };
			  immediate=op->offset; anyimm=op->anyoffset;
			  if (pd->bits==SS || pd->bits==WS) {
				  if (datasize>1 && (constsize & 2)!=0 &&
					  ((immediate>=-128 && immediate<128) || op->anyoffset!=0)) {
						  immsize=1; tcode[i]|=0x02; }
				  else immsize=datasize;
				  tmask[i]|=0x02; }
			  else immsize=datasize;
			  break;
	  case IMX:                        // Immediate sign-extendable byte
	  case IMS:                        // Immediate byte (for shifts)
	  case IM1:                        // Immediate byte
		  if (immsize==2)                // To accomodate ENTER instruction
			  immediate=(immediate & 0xFFFF) | (op->offset<<16);
		  else immediate=op->offset;
		  anyimm|=op->anyoffset;
		  immsize++; break;
	  case IM2:                        // Immediate word (ENTER/RET)
		  immediate=op->offset; anyimm=op->anyoffset;
		  immsize=2; break;
	  case IMA:                        // Immediate absolute near data address
		  if (op->segment!=SEG_UNDEF && op->segment!=SEG_DS)
			  segment=op->segment;
		  displacement=op->offset; anydisp=op->anyoffset;
		  dispsize=4; break;
	  case JOB:                        // Immediate byte offset (for jumps)
		  jmpoffset=op->offset; anyjmp=op->anyoffset;
		  jmpsize=1; break;
	  case JOW:                        // Immediate full offset (for jumps)
		  jmpoffset=op->offset; anyjmp=op->anyoffset;
		  jmpsize=4; break;
	  case JMF:                        // Immediate absolute far jump/call addr
		  displacement=op->offset; anydisp=op->anyoffset; dispsize=4;
		  immediate=op->segment; anyimm=op->anyoffset; immsize=2;
		  break;
	  case SGM:                        // Segment register in ModRM byte
		  hasrm=1;
		  if (op->index<6) {
			  tcode[i+1]|=(char)(op->index<<3); tmask[i+1]|=0x38; };
			  break;
	  case SCM:                        // Segment register in command byte
		  if (op->index==SEG_FS || op->index==SEG_GS) {
			  tcode[0]=0x0F; tmask[0]=0xFF;
			  i=1;
			  if (strcmp(name,"PUSH")==0)
				  tcode[i]=(char)((op->index<<3) | 0x80);
			  else
				  tcode[i]=(char)((op->index<<3) | 0x81);
			  tmask[i]=0xFF; }
		  else if (op->index<6) {
			  if (op->index==SEG_CS && strcmp(name,"POP")==0) {
				  strcpy(errtext,"Unable to POP CS");
				  goto error; };
				  tcode[i]=(char)((tcode[i] & 0xC7) | (op->index<<3)); }
		  else {
			  tcode[i]&=0xC7;
			  tmask[i]&=0xC7; };
			  break;
	  case PRN:                        // Near return address (pseudooperand)
	  case PRF:                        // Far return address (pseudooperand)
	  case PAC:                        // Accumulator (AL/AX/EAX, pseudooperand)
	  case PAH:                        // AH (in LAHF/SAHF, pseudooperand)
	  case PFL:                        // Lower byte of flags (pseudooperand)
	  case PS0:                        // Top of FPU stack (pseudooperand)
	  case PS1:                        // ST(1) (pseudooperand)
	  case PCX:                        // CX/ECX (pseudooperand)
	  case PDI:                        // EDI (pseudooperand in MMX extentions)
		  break;                         // Simply skip preudooperands
	  default:                         // Undefined type of operand
		  strcpy(errtext,"Internal Assembler error");
		  goto error;
														 };
													 };
													 // Gather parts of command together in the complete command.
													 j=0;
													 if (lock!=0) {                       // Lock prefix specified
														 model->code[j]=0xF0;
														 model->mask[j]=0xFF; j++; };
														 if (datasize==2 && pd->bits!=FF) {   // Data size prefix necessary
															 model->code[j]=0x66;
															 model->mask[j]=0xFF; j++; };
															 if (addrsize==2) {                   // Address size prefix necessary
																 model->code[j]=0x67;
																 model->mask[j]=0xFF; j++; };
																 if (segment!=SEG_UNDEF) {            // Segment prefix necessary
																	 if (segment==SEG_ES) model->code[j]=0x26;
																	 else if (segment==SEG_CS) model->code[j]=0x2E;
																	 else if (segment==SEG_SS) model->code[j]=0x36;
																	 else if (segment==SEG_DS) model->code[j]=0x3E;
																	 else if (segment==SEG_FS) model->code[j]=0x64;
																	 else if (segment==SEG_GS) model->code[j]=0x65;
																	 else { strcpy(errtext,"Internal Assembler error"); goto error; };
																	 model->mask[j]=0xFF; j++; };
																	 if (dispsize>0) {
																		 memcpy(tcode+i+1+hasrm+hassib,&displacement,dispsize);
																		 if (anydisp==0) memset(tmask+i+1+hasrm+hassib,0xFF,dispsize); };
																		 if (immsize>0) {
																			 if (immsize==1) l=0xFFFFFF00L;
																			 else if (immsize==2) l=0xFFFF0000L;
																			 else l=0L;
																			 if ((immediate & l)!=0 && (immediate & l)!=l) {
																				 strcpy(errtext,"Constant does not fit into operand");
																				 goto error; };
																				 memcpy(tcode+i+1+hasrm+hassib+dispsize,&immediate,immsize);
																				 if (anyimm==0) memset(tmask+i+1+hasrm+hassib+dispsize,0xFF,immsize); };
																				 i=i+1+hasrm+hassib+dispsize+immsize;
																				 jmpoffset=jmpoffset-(i+j+jmpsize);
																				 model->jmpsize=jmpsize;
																				 model->jmpoffset=jmpoffset;
																				 model->jmppos=i+j;
																				 if (jmpsize!=0) {
																					 if (ip!=0) {
																						 jmpoffset=jmpoffset-ip;
																						 if (jmpsize==1 && anyjmp==0 && (jmpoffset<-128 || jmpoffset>=128)) {
																							 if (longjump==0 && (jmpmode & 0x03)==0) {
																								 longjump=1;
																								 goto retrylongjump; };
																								 sprintf(errtext,
																									 "Relative jump out of range, use %s LONG form",name);
																								 goto error; };
																								 memcpy(tcode+i,&jmpoffset,jmpsize);
																					 };
																					 if (anyjmp==0) memset(tmask+i,0xFF,jmpsize);
																					 i+=jmpsize; };
																					 memcpy(model->code+j,tcode,i);
																					 memcpy(model->mask+j,tmask,i);
																					 i+=j;
																					 model->length=i;
																					 return i;                            // Positive value: length of code
error:
																					 model->length=0;
																					 return cmd-asmcmd;                   // Negative value: position of error
};

//#pragma option -O.                     // Restore old optimization options

#pragma warning(default:4996)  
#pragma warning(default:4244)
#pragma warning(default:4267)